//---------------------------------------------------------------------
// Copyright (C) 2010 - Cobalt Solution / Sebastien Leon              |
//---------------------------------------------------------------------
// This software is under dual licence: commercial licence + GPL v3   |
// To keep closed your modification of this software, you need to     |
// contact me to obtain a commercial licence.                         |
// Any other derivated work should remain Open Source.                |
// Contact me for a licence, a question or if you want to commit      |
// your code to the main tree: seb at cobaltsolutions dot info        |
//---------------------------------------------------------------------

#ifndef _CORE_H_
#define _CORE_H_

#define LOG_MSG_QUOTA 20 // number max of log messages per Loom

template<class T> T   min  (const T& i, const T& j) { return ((i < j) ? i : j); }
template<class T> T   max  (const T& i, const T& j) { return ((i > j) ? i : j); }

#include <QSettings>
#include <QHostAddress>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QColor>
#include <QPoint>
#include <QFtp>
#include <QBuffer>
#include <QThread>
#include <QMessageBox>
#include <QSystemTrayIcon>

class QtTelnet;

class Loom;
class QFileInfo;
class FTPThread;
class TELNETThread;

/**
 This class is the core class of Loomfeed.
 No GUI is required. It should run eventually as 
 a service and/or should provide all the info to
 some other interfaces (direct Gui with mainFrom,
 remote request, etc...)
**/
class Core : public QObject
{
  Q_OBJECT


private:
  class Resource
  {
  public:
    Resource(int id, const QString& n, const QString& p):m_id(id),m_count(0),m_name(n),m_path(p){};
    Resource& operator = (const Resource& r) {m_id=r.m_id;m_count=r.m_count;m_name=r.m_name;m_path=r.m_path; return *this;}

    QString m_path;
    QString m_name;
    int     m_id;
    int     m_count;
  };

public:
  Core();
  ~Core();

  static Core* Get();

  enum EReportType { e_Svg, e_Pdf };
  enum EModifType
  {
    e_loomAdded         = 1,
    e_loomWillBeDeleted = 2,
    e_nameChanged       = 4,
    e_infoChanged       = 8,
    e_addressChanged    = 16,
    e_loomPathChanged   = 32, // This is only sub loom path (not core path)
    e_graphicChanged    = 64, // Can be Size, Position, Color
    e_loomChanged       = e_nameChanged | e_infoChanged | e_addressChanged | e_loomPathChanged | e_graphicChanged
  };

  // AddLoom : newLoom will be added to the list... if AddLoom returns true, the Core class will manage it (including freeing if when necessary)
  // AddLoom will return false if this loom is already in the list or if another loom has the same address or name
  bool AddLoom         (Loom* p_l);

  // DeleteLoom will remove this Loom from the list (will return false if this loom is NOT found in Core list)
  bool DeleteLoom      (const Loom* p_l);

  // UpdateLoom will update p_oldLoom by the values from updated loom (updatedLoom is untouched and can be deleted after the function call)
  // (return false if oldLoom is not found or if another loom has already this name or address)
  bool UpdateLoom      (const Loom* p_oldLoom, const Loom& updatedLoom);

  bool ValidLoomGraphicChange (const Loom* p_Loom);

  bool ChangeBkgColor  (const QColor&  col );
  bool ChangePath      (const QString& path);
  bool ChangeMonitoring(bool monitoring) { m_monitoringCore = monitoring; return true; }
  bool ChangeReporting (bool reporting ) { m_reportingCore  = reporting ; return true; }

  const QColor       Color() const {return m_bkgColor; }
  const QString&     Path () const {return m_path;     }
  QString            Path (const QHostAddress& add) const; // Find the correct Loom and combine its path and Core::path
  const QList<Loom*> Looms() const {return m_loomList; }

  void  InitiateTransfer (QFileInfo& nfo, const Loom* p_loom = NULL); // NULL => all possible looms

  const Loom*        FindLoomByIP(const QHostAddress& address);
  QGraphicsScene&    LoomScene() { return m_loomScene; }

  QString GetResourceName   (int id);
  QString GetAndLockResource(int id); // will increment the internal counter
  bool    IsMonitoring      () { return m_monitoringCore && m_licenceValid; }
  bool    IsReporting       () { return m_reportingCore  && m_licenceValid; }
  bool    IsValidLicence    () { return m_licenceValid;   }
  bool    Monitoring        () { return m_monitoringCore; }
  bool    Reporting         () { return m_reportingCore;  }

  void SaveSettings();

  QString CreateReport(EReportType type = e_Pdf);

signals:
  void Message         (const QString&, QSystemTrayIcon::MessageIcon);
  void PathModified    ();
  void BkgColorModified();
  void LoomChanged(const Loom&, EModifType); // This loom has been modified (or will be... for e_loomWillBeDeleted)
  void GeneralInfo(const QString& msg);

public slots:

  void UnlockResource (FTPThread&); // Usually a FTPThread which is terminated
  void StartTelnetConnexions ();
  void CheckLicence          ();
  void ReportingReq          ();
  void done            ( bool error );
  void requestFinished ( int id, bool error );

private:
  Resource* GetResourceFromID(int id);

  QSettings       m_settings;
  QGraphicsScene  m_loomScene;

  QString         m_path;
  QColor          m_bkgColor;
  QList<Loom*>    m_loomList;
  QList<Resource> m_resourceList;
  int             m_resourceID; // counter

  QByteArray      m_licenceReq;
  QBuffer         m_licenceBuffer;
  bool            m_monitoringCore;
  bool            m_reportingCore;
  bool            m_licenceValid;
  QByteArray      m_reportingImg;
  QBuffer         m_reportingBuffer;

  QFtp            m_reportingFTP;
};
//----------------------------------------------------
//----------------------------------------------------
class Loom : public QGraphicsObject
{
  Q_OBJECT
public:
  Loom (const Loom& l);
  Loom ();
  ~Loom ();

  enum ELoomState { e_connecting, e_not_a_JC, e_jcFound, e_error, e_locked, e_stopped_backward, e_stopped_forward, e_running, e_weaving, e_undefined };
  enum EFTPState  { e_noFTP, e_connectFTP, e_errorFTP, e_okFTP };

  Loom& operator = (const Loom& l); // deep copy

  bool  IsInCaption (const QPointF& p) const;
  bool  IsInBody    (const QPointF& p) const;
  bool  IsInResize  (const QPointF& p) const;
  const bool          IsEnabled () const { return m_enabled;   }
  const bool          Monitoring() const { return m_monitoring;}
  const QString&      Name      () const { return m_name;      }
  const QHostAddress& Address   () const { return m_address;   }
  const QString&      Info1     () const { return m_info1;     }
  const QString&      Info2     () const { return m_info2;     }
  const QColor&       Color     () const { return m_boxColor;  }
  const QPointF       Pos       () const { return pos();       }
  const QSizeF&       Size      () const { return m_size;      }
  const QString&      SrcPath   () const { return m_path;      } // relative path to Core::Path() | QString null means = than Core::path
  const QStringList&  Log       () const { return m_log;       }
  const char*         Login     () const { return "staubli";   } 
  const char*         Password  () const { return "";          } 
  const char*         DstPath   () const { return "/users";    } 
  const QList<FTPThread*>& GetPendingList() const { return m_threadList; }

  void AlignGrid ();
  void SetEnabled   (const bool         enabled   ) { m_enabled    = enabled;           update(); };
  void SetMonitoring(const bool        monitoring ) { m_monitoring = monitoring;        update(); };
  void SetName      (const QString&     name      ) { m_name       = name.simplified(); update(); };
  void SetInfo1     (const QString&     info      ) { m_info1      = info      ;        update(); };
  void SetInfo2     (const QString&     info      ) { m_info2      = info      ;        update(); };
  void SetColor     (const QColor&      colorOfBox) { m_boxColor   = colorOfBox;        update(); };
  void SetAddress   (const QHostAddress&address   ) { m_address    = address; }; // no graphic change (update is not necessary)
  void SetPath      (const QString&     path      ) { m_path       = path   ; }; // no graphic change (update is not necessary)
  void SetPos       (const QPointF&     pos       ); // more complexe check - not inlined
  void SetSize      (const QSizeF&      size      ); // more complexe check - not inlined
  void UpdateState  (ELoomState s, const QString& info = QString::null);

  bool IdenticalNameOrAddress (const Loom&       l) const;

  void SaveLoomInSettings     (QSettings& settings) const;
  void LoadLoomFromSettings   (const QSettings& settings);

  // This function will add a new entry to the list of log events.
  // Use QMessageBox::Information (default) for Green entry. QMessageBox::Warning for Yellow entry
  // QMessageBox::Critical for Red entry. Other code will display black.
  // An automatic DateTime Stamp will be added to the message...
  // Oldest entries will be removed if the log quota is exceeded (LOG_MSG_QUOTA)
  void AddLogEntry (const QString& caption, const QString& msg, QMessageBox::Icon msgType = QMessageBox::Information);
  //== GraphicsItem virtual:
  QRectF boundingRect() const;
  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

  void AddThread (FTPThread* p_thread); // Add this thread to the list. Start it if there is no other...
  void NextThread(FTPThread* p_thread = NULL); // Remove this one (if any) and start the firt one available...
  static const int Fc = 96. / 2.54; // Correction factor (cm/points)
//  virtual void mouseDoubleClickEvent ( QGraphicsSceneMouseEvent * event );

  bool HasTelnetCom     () const { return (bool)mp_telnet; }
  void RestartTelnetCom ();
  ELoomState GetState   () const { return m_state; }
  bool IsMonitoring     () { return m_monitoring && Core::Get()->IsMonitoring();}
protected:
  bool         m_enabled;
  bool         m_monitoring;
  QString      m_name;
  QHostAddress m_address;
  QString      m_info1;
  QString      m_info2;
  QColor       m_boxColor;
  QSizeF       m_size;
  QString      m_path;
  // Upload Management: (not persistant info)
  QList<FTPThread*> m_threadList;
  QStringList  m_log;
  // Current downloading: (see TransferInProgress)
  QString      m_ftpName;  // empty => just a green or red [T] near loom name ; otherwise we use info area
  float        m_ftpProg;  // 0: start - 1.0: end
  EFTPState    m_ftpState;
  // State management:
  QtTelnet*    mp_telnet;
  ELoomState   m_state; // + sync info:  SYNC
  QString      m_info;  // ex: "test.jc5" or "<aucun>" for NO FILE
  int          m_timerID;
  int          m_connectTry;

  void ReleaseTelnet();
  void InitResize();
  static QPixmap* msp_resize;

  virtual void timerEvent ( QTimerEvent * event );
protected slots:
  void TransferInProgress (const int = (int)e_connectFTP, const QString& name = QString::null, const float progress = 0.0); // Warning ! Usually called from another thread

  // Slots for Telnet signals:
  void connectionError ( QAbstractSocket::SocketError error );
  void loggedIn        ();
  void loginFailed     ();
  void loginRequired   ();
  void message         ( const QString & data );
};

#endif // _CORE_H_